"use strict";
/* 字符串的截取替换开始 */
function phone(mobile, length) {
	return String(mobile).slice(0, length * -1) + "*".repeat(length);
}
console.log("字符串的替换：" + phone("192847586940", 3))
/* 字符串的截取替换结束 */

/* 取随机数开始 */
function a1() {
	let a = 4.3;
	console.log("向上整数：" + Math.ceil(a));
	console.log("向下整数：" + Math.floor(a));
	/*
	  0<=random<1
	  min+Math.floor(Math.random()*(max-min+1))
	  包括5的话后面加1，不包括不加
	 */
	console.log("随机数2~5：" + (2 + Math.floor(Math.random() * (5 - 2) + 1)));
}
a1();
/* 取随机数结束 */

/* 日期对象时间戳的转换开始 */
function a2() {
	const date = new Date("1996-7-12 08:22:12")
	console.log("原始时间:" + date);
	console.log("原始时间:" + date.toLocaleString());
	console.log("方式一转换成时间戳：" + date.getTime());
	console.log("方式二转换成时间戳：" + date * 1);
	console.log("方式三转换成时间戳：" + date.valueOf());
	console.log("方式四转换成时间戳：" + Number(date));
	const timestamp = date.valueOf();
	console.log("时间戳转换成标准时间：" + new Date(timestamp));
}
a2();
/* 日期对象时间戳的转换结束 */

/* 操作DOM对象开始 */

// 修改文本值,似乎只能通过id来修改文本内容
var wen1 = document.getElementById("b1");
wen1.innerHTML = "90123";

//操作css
var wen2 = document.getElementById("b3");
wen2.style.color = "red";

//删除节点步骤：先获取父节点，在通过父节点删除自己
var self = document.getElementById("b1");
var father = b1.parentElement;
// father.removeChild(father.children[1]);
father.removeChild(self);

//创建和插入DOM节点
// 追加节点已有节点
var zui = document.getElementById("zui");
var zui1 = document.getElementById("a1");
zui1.appendChild(zui);
//创建并追加节点
var newChuang = document.createElement('p'); //创建一个p标签
newChuang.id = 'newChuang'; //加id
newChuang.innerHTML = "这是创建的一个节点"; //加内容
zui1.appendChild(newChuang); //追加

var newScript = document.createElement('script'); //创建一个script标签
//newScript.type="text/javascript";
newScript.setAttribute("type", "text/javascript"); //加对应的类型
document.getElementsByTagName("body")[0].appendChild(newScript); //追加子节点


/* 操作DOM对象结束 */

/* 打印99乘法表开始 */
function chengFaBiao() {
	for (let i = 1; i <= 9; i++) {
		for (let j = 1; j <= i; j++) {
			let str = j + "*" + i + "=" + (i * j);
			document.write(str + "&nbsp;&nbsp;&nbsp;");
		}
		document.write("<br/><br/>")
	}
}
chengFaBiao();
/* 打印99乘法表结束 */

/* 箭头函数开始 */
let sum = [1, 2, 3].reduce(function (a, b) {
	return a + b;
});
console.log("箭头函数：" + sum);

//变成箭头函数时function可以去了
let sum1 = [1, 2, 3].reduce((a, b) => a + b);
console.log("箭头函数1：" + sum1);
/* 箭头函数结束 */

/* 递归开始 */
//递归就是让它重复的做一件事情(重复调用自己)，然后再合适的时机把它返回
function factorial(num) {
	//找到时机退出
	if (num == 1) {
		return 1;
	}
	//不断循环
	return num * factorial(num - 1);

	/* 
	 原理就是知道factorial(num-1)的最后一个值时再一步步推回来
	 
	 return 3*factorial(2)  //当num=3时
	 return 2*factorial(1)  //当num=2时
	 return 1 //返回到上一层   //当num=1时
	 return 2*1 //在返回到上一层
	 return 3*2*1 //知道最后的确定值，最终结果返回
	 */
}
console.log("递归:" + factorial(3)); //3*2*1
/* 递归结束 */

/* 展开语法（点语法）开始 */
//在等号右边是把他们分开，再左边是合在一起
function sum3(...args) {
	console.log(args);
	return args.reduce((a, b) =>  a+b);
}
console.log("展开语法（点语法）：" + sum3(2, 34, 21, 5, 2));
/* 展开语法（点语法）结束 */

/* 对象开始 */
let user = {
	name: "清尘",
	grade: [{
			name: "js",
			score: 99
		},
		{
			name: "docker",
			score: 88
		}
	],
	average: function () {
		let total = this.grade.reduce((m, n) => m + n.score, 0);
		// return `${this.name}的平均成绩是：${total/this.grade.length}`;
		return this.name + "的平均成绩是：" + total / this.grade.length;
	}
};
console.log(user.average());


/* 
	深拷贝浅拷贝
 */
//原始的地址复制，会把原来的数据改变
let hd11 = {
	name: "原始值"
};
let cms11 = hd11;
cms11.name = "现在值";
console.log(hd11); //现在值
// 浅拷贝:它可以把原来的数据进行复制(通过地址)，相当于开辟了一个新的地址，你给它赋了一个新的值，他不会改变原来的
// 浅拷贝只执行一层，不能进行深层次的复制
//Object.assign 函数可简单的实现浅拷贝，它是将两个对象的属性叠加后面对象属性会覆盖前面对象同名属性
let user1 = {
	name: '后盾人'
};
let hd = {
	stu: Object.assign({}, user1)
};
hd.stu.name = 'hdcms';
console.log(user1.name); //后盾人

//使用展示语法也可以实现浅拷贝
let obj = {
	name: "后盾人"
};
let hd1 = {
	...obj
};
hd1.name = "hdcms";
console.log("展示语法：");
console.log(hd1.name); //hdcms
console.log(obj.name); //后盾人
//使用for/in执行对象拷贝
let obj2 = {
	name: "后盾人"
};
let hd2 = {};
for (const key in obj2) {
	console.log(key);
	hd2[key] = obj2[key];
}
hd2.name = "hdcms";
console.log("for-in：");
console.log(hd2); //hdcms
console.log(obj2); //后盾人

//深拷贝，通过一个函数来递归
//纯对象
let obj3 = {
	name: '后盾人',
	user: {
		name: 'hdcms'
	}
}

function copy(object) {
	let obj3 = {}
	for (const key in object) {
		// obj3[key] = object[key]; //浅拷贝
		//深拷贝，递归
		obj3[key] = typeof object[key] == 'object' ? copy(object[key]) : object[key];
	}
	return obj3;
}
let newObj = copy(obj3);
newObj.name = 'hdcms';
newObj.user.name = '湘军';
console.log(JSON.stringify(obj3, null, 2));
console.log(JSON.stringify(newObj, null, 2));


//有数组
let obj4 = {
	name: "后盾人",
	user: {
		name: "hdcms"
	},
	data: []
};

function copy(object) {
	//判断对象是一个数组还是一个函数
	let obj4 = object instanceof Array ? [] : {};
	for (const [k, v] of Object.entries(object)) {
		obj4[k] = typeof v == "object" ? copy(v) : v;
	}
	return obj4;
}
let hd4 = copy(obj4);
hd4.data.push("向军");
console.log("1:" + JSON.stringify(hd4, null, 2));
console.log(JSON.stringify(obj4, null, 2));


/* 对象结束 */

/* 作用域 */
/* 
	var 是属于全局的，后面的值会覆盖前面的值，所以会看到五个都是5
	而let有自己的作用域块，因为在for循环中使用了let声明循环变量，所以每一个值都会存在于单独的作用域不会被覆盖掉
*/
for(var i=0;i<5;i++){
	setTimeout(()=>{
		 console.log("作用域："+i);// 5个5
	 },100)
 }
 console.log(i);// 5

 for(let j=0;j<5;j++){
	setTimeout(()=>{
		 console.log(j);// 0,1,2,3,4
	 },100)
 }

/* 闭包开始 */
console.log("闭包：");
//下面的形式会导致函数执行完成后counter被释放掉，所以要用闭包
function add() {
	var counter = 0;
	//它会每执行一次就重新调用返回，返回的是一个值
	return counter += 1;
}
console.log(add());
console.log(add());
console.log(add());


function outerFunction() {
	var counter = 0;

	function innerFunction() {
		return counter += 1;
	}
	return innerFunction;
	/*
	 注意 typeof innerFunction 是：function；而typeof innerFunction（）是number；
	*/
}
var add1 = outerFunction(); //其实最后赋值给add的是return counter += 1 这段代码。
/* 
调用 outerFunction()返回的是内部函数innerFucntion,那么调用几次add()将调用几次
内部函数inner Function，内部函数公用了counter，所以能够计数,所以说闭包就是将内部嵌套函数变成外部可调用的。
*/
console.log(add1());
console.log(add1());
console.log(add1());

//以上代码可写为如下形式
var add2 = function () {
	var counter = 0;
	console.log("父方法执行一次"); // 只有在 add 赋值时执行一次 
	return function () {
		console.log("子方法执行"); // 每次执行 add() 都会执行
		return counter += 1;
	}
	// counter 作用域在父函数中, 自然在其子函数中也能使用,但因为
	// 子函数还需要使用了count, 所以 count 不随着父函数一起释放。    
	// 利用在 function(){}() 的形式自动执行一遍父匿名函数, 赋给 add 子方法。
}();
console.log(add2());
console.log(add2());
console.log(add2());
/* 闭包结束 */
/* 异步封装setTimeout开始 */
function timeout(time) {
	return new Promise(resolve => setTimeout(resolve, time));
}
timeout(3000)
	.then(() => { //then对应返回的上一个Promise对象
		console.log("3秒后执行");
		return timeout(2000);
	})
	.then(() => {
		console.log("执行上一步的promise后1秒执行");
	})

/* 异步封装setTimeout开始 */

//异步promise对象
// 什么情况下会用到Promise?
    // 一般情况下是有异步操作时,使用Promise对这个*异步操作进行封装*
    // new -> 构造函数(1.保存了一些状态信息  2.执行传入的函数)
    // 在执行传入的回调函数时, 会传入两个参数, resolve解决, reject拒绝.这两个本身又是函数
    // 参数 -> 函数((resolve, reject)
 
    // 以后有什么异步的操作的话都可以封装在Promise里面
	new Promise((resolve, reject) => {
 
		setTimeout(() => {
   
		  // 如果有代码需要处理不会直接这样放这里 在then里面处理data
		  // console.log('hello world');
		  // console.log('hello world');
		  // console.log('hello world');
		  // console.log('hello world');
   
		  // 成功的话去到then那里
		  // resolve('Hello World')
   
		  // 失败的时候调用reject 会去到catch那里
		  reject('error message')
		}, 1000)
	  }).then((data) => { // then里面也是一个函数
		// 1.100行的处理代码
		console.log(data);
		console.log(data);
		console.log(data);
		console.log(data);
		console.log(data);
	  }).catch((err) => {
		console.log(err);
	  })
	  // 通过回调里的 resolve(data) 将这个 promise 标记为 resolverd，
	  // 然后进行下一步 then((data)=>{//do something})，resolve 里的参数就是你要传入 then 的数据。

